/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package myipaddress;

/** Класс IP-адреса
 *
 * @author Роман Ермолов
 */
public class MyIp4Address {
        
    private int ip_address[];   //ip-адрес
    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) {
        // TODO code application logic here
    }
    
    //Конструктор   
    
   //сделать выброс в part и конструкторе
    
    /** Создаёт адрес из строки вида "192.168.1.1"
     * 
     * @param a адрес в виде строки
     */
    public MyIp4Address(String address){
        this.ip_address = new int[4];   //переменная класса 
        
        int i;                          //счётчик
        Boolean ip_valid = false;       //валидность ip-адреса
        
        //проверяем внешнее соответствие адреса( помощью регэкспа можно проверить только точки и части > 0);
        if(address.matches("[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}")){
            
            String address_part[] = address.split("\\.");   //части ip адреса
            ip_valid = true;
            
            //записываем адрес по частям
            for(i = 0; i < 4; i++){
                int p = Integer.parseInt(address_part[i]);
                if(p > 255)
                    ip_valid = false;
                else 
                    this.ip_address[i] = p;
            }            
        }   
        //если строка не валидная
        if(!ip_valid)   
            for(i = 0; i < 4; i++)
                this.ip_address[i] = -1;
        
    }
    
    //Свойства
    
    /** Геттер адреса
     * 
     * @return адрес в виде строки
     */
    public String address(){
        
        return Integer.toString(this.ip_address[0]) + "." 
                + Integer.toString(this.ip_address[1]) + "."
                + Integer.toString(this.ip_address[2]) + "."
                + Integer.toString(this.ip_address[3]);
    }
    
    /** Геттер части адреса
     * 
     * <br><p>pre: 0 <= i and i < 4</p>
     * <p>post: self.ip_address[i];</p>
     * 
     * @param i номер части
     * @return часть адреса
     */
    public int part(int i) throws IndexOutOfBoundsException{
        //если i вне границ, выбрасываем exception
        if(i < 0 || i >= 4) 
            throw new IndexOutOfBoundsException("part: i out of bounds\n");
        
        return this.ip_address[i];
    }
    
    //Методы
    
    /** Метод получает адрес сети для данного ip-адреса при указанной маске подсети
     * 
     * @param mask  маска подсети
     * @return адрес сети
     */
    public String getNetworkAddress(String mask_string){
        MyIp4Address mask = new MyIp4Address(mask_string);
 
        //проверяем маску
        if(mask.isValid()){
            MyIp4Address network = new MyIp4Address("0.0.0.0");
            
            //делаем побитовое И
            for(int i = 0; i < 4; i++)
                network.ip_address[i] = ip_address[i]&mask.ip_address[i];
            
            return network.address();
        }
        else
            return("0.0.0.0");
        
    }   
   
    /** Метод возвращает правильность ip-адреса
     * <br><p>post: 0<=self.part(0)<=256 and 0<=self.part(1)<=256</p>
     * <p>and 0<=self.part(2)<=256 and 0<=self.part(3)<=256</p>
     * @return правильность ip-адреса
     */
    public boolean isValid(){
        if(ip_address[0] != -1 && ip_address[1] != -1 && ip_address[2] != -1 && ip_address[3] != -1)
            return true;
        return false;
    }
    
      /** Возвращает копию из текущего экземпляра
     * 
     * @param other существующий экземпляр
     */
    @Override
    public MyIp4Address clone(){
        
        return new MyIp4Address(this.address());         
    }
    
    /** Метод проверяет на эквивалентность ip-адреса
     * 
     * <br><p>post: self.part(0) = other.part(0) and self.part(1) = other.part(1)</p>
     * <p>and self.part(2) = other.part(2) and self.part(3) = other.part(3)</p>
     * @param other другой объект
     * @return эквивалентность
     */
    public boolean equals(MyIp4Address other){
        //если адреса одинаковые и оба адреса валидны
        if(this.isValid() == true && other.isValid() == true && this.address().equals(other.address()))
            return true;
        return false;
    }
    
    /** Метод проверяет, меньше ли один ip-адрес, чем другой
     * 
     * <br><p>post: сравнить поочёрёдно part(i) и определить, какой из адресов меньше</p>
     * @param other другой объект
     * @return меньше ли текущий объект, чем other
     */
    public boolean lessThan(MyIp4Address other) {  
        int i = 0;
        
        //сравниваем по частям(слева направо)
        while(i < 4){
            if(this.ip_address[i] < other.ip_address[i])
                return true;
            else if(this.ip_address[i] > other.ip_address[i])
                return false;
            else 
                i++;
        }
        return false;
    }
    
    /** Метод возвращает ip компьютера, равный текущему + c
     * 
     * <br><p>post:новый адрес, если он входит в ту же подсеть. иначе старый адрес</p>
     * <p>pre: offset > 0; <p>
     * @param offset количество компьютеров
     * @param mask маска подсети(для проверки)
     * @return новый экземпляр класса
     */
    public MyIp4Address add(int offset, String mask_string){
        
        int i = 0;
        MyIp4Address new_address = this.clone(); //новый адрес
        MyIp4Address mask = new MyIp4Address(mask_string);      //маска
        
        //проверяем, валидная ли маска
        if(mask.isValid() && offset >= 0){
            new_address.ip_address[3] += offset;
            
           i = 3; 
           while(i > 0 && new_address.ip_address[i] > 255){
               new_address.ip_address[i - 1] += new_address.ip_address[i] / 256;
               new_address.ip_address[i] = new_address.ip_address[i] % 256;
               i--;
            }
                   
           //если перенос получился правильным и маска одинаковая - возвращаем полученный экземпляр
           if(new_address.isValid() && new_address.getNetworkAddress(mask_string).equals(this.getNetworkAddress(mask_string)))
               return new_address;               
        } 

        //возвращаем копию текущего
        return this.clone();
    }
}
